import { useMemo } from "react";
import { URLSearchParamsInit, useSearchParams } from "react-router-dom";
import { cleanObject } from "utils";

// 返回页面URL中指定键的参数值
// 问题 1
// 返回是不仅要包括当前url的参数键值对，还需要返回改变参数的方法setSearchParams
// setSearchParams 用来改变url上的参数值
export const useUrlQueryParam = <K extends string>(keys: K[]) => {
  const [searchParams] = useSearchParams();
  const setSearchParams = useSetSearchUrlParam();
  // 将数组转换成键值对
  return [
    // 问题 2
    // prev: [x: string]: string; searchParams.get(next)返回的类型是string|null
    // 所以对key赋值的时要判断null并置''
    // 问题 3
    // reduce这里返回的类型是{}，但是不符合真正的类型，需要手动将它变成
    // { [key in string]: string }
    // 使用useMemo防止无限循环
    useMemo(
      () =>
        keys.reduce((prev, key) => {
          return {
            ...prev,
            [key]: searchParams.get(key) || "",
          };
        }, {} as { [key in K]: string }),
      // eslint-disable-next-line react-hooks/exhaustive-deps
      [searchParams]
    ),
    (params: Partial<{ [key in string]: unknown }>) => {
      return setSearchParams(params)
    }
    // 问题 4
    // 对于tuple类型，ts会将里面的元素类型统一化，所以当出现不同类型时就会用|来涵盖所有类型
    // 这样显得类型很乱，通常对于这种问题可以在后面添加 as const解决，添加之后的类型就时特定的了
  ] as const;
};

export const useSetSearchUrlParam = () => {
  const [searchParams, setSearchParam] = useSearchParams();
  return (params: { [key in string]: unknown }) => {
    const o = cleanObject({
      ...Object.fromEntries(searchParams),
      ...params,
    }) as URLSearchParamsInit;
    return setSearchParam(o);
  }
}
